#!/usr/bin/env python
# -*- coding: utf-8 -*-

##@package DBDaemon
# @author Bretton Murphy
# @author Jeremiah Davis
# @author Ricky the WunderMoose(TM)
# @author A pack of Himilayan Llamas
# @date Spring 2011
#
# This module is a database daemon that establishes a connection to a 
# socket with a given IP and port number pair.  Once connected and the run command is given
# the daemon will read in packets of data coming in from a serial forwarder
# and insert that data into a given database.
#

import socket
import os
import ConfigParser
import DataParser
import psycopg2
from datetime import datetime
import sys
import adapt_table
import getopt

## Main function of DBDaemon
#
# This Function is the entry point for the DBDaemon program.
# It takes the following command line options:
# -a --address   The IP address or URL for the Serial Forwarder to connect to
# -p --port      The port number the serial forwarder is on.
# -A --db-address The URL of the database to connect to.
# -d --database  The database to connect to
# -U --db-user   The database user to log in as.
# -P --db-pass   The password for the database.
# -c --config-file    The path to the configuration file to use instead of default.
# -s --db-size	 The size of the database
def main(argv):

    clopts = {'so-address':'localhost', 'port':9002, 'db-address':'localhost', 
              'database':'drawbridge', 'db-user':'drbgdaemon', 'db-pass':'secret', 'db-size':2048}

    configName = ".drawbridge"
    path = None

    # Process command line arguments
    try:
        opts,args = getopt.getopt(argv, "a:p:A:d:U:P:s:",
                                  ["so-address=","port=","db-address=",
                                   "database=","db-user=","db-pass=","db_size="])
    except getopt.GetoptError:
        print "Invalid arguments"
        usage()
    
    if len(args) > 1:
        print "Invalid argument:"
        print args
        usage()
    elif len(args) == 1:
	configName = args[0]
	print "Changed configuration file to %s" % configName
    
    if 'HOME' in os.environ:
	path = "%s/%s" % (os.environ['HOME'], configName)
	print "Created a path to %s" % path
    if os.path.isfile(path):
	config = ConfigParser.RawConfigParser()
	config.read(path)
	print "Successfully read configuration file"
	if config.has_option('Credentials', 'so-address'):
	    clopts['so-address'] = config.get('Credentials', 'so-address')	
	if config.has_option('Credentials', 'port'):
	    clopts['port'] = int(config.get('Credentials', 'port'))	
	if config.has_option('Credentials', 'db-address'):
	    clopts['db-address'] = config.get('Credentials', 'db-address')	
	if config.has_option('Credentials', 'database'):
	    clopts['database'] = config.get('Credentials', 'database')	
	if config.has_option('Credentials', 'db-user'):
	    clopts['db-user'] = config.get('Credentials', 'db-user')	
	if config.has_option('Credentials', 'db-pass'):
            clopts['db-pass'] = config.get('Credentials', 'db-pass')
	if config.has_option('Credentials', 'db-size'):
            clopts['db-size'] = config.get('Credentials', 'db-size')

    for opt,arg in opts:
        if opt in ( "-a", "--so-address"):
            clopts['so-address'] = arg
        elif opt in ("-p", "--port"):
            clopts['port'] = int(arg) 
        elif opt in ("-A", "--db-address"):
            clopts['db-address'] = arg
        elif opt in ("-d", "--database"):
            clopts['database'] = arg
        elif opt in ("-U", "--db-user"):
            clopts['db-user'] = arg
        elif opt in ("-P", "--db-pass"):
            clopts['db-pass'] = arg
        elif opt in ("-s", "--db-size"):
            clopts['db-size'] = int(arg)
        
	
    #    print clopts

    # Create a daemon object, connect to serial forwarder socket
    # and to the database.
    d = DBDaemon()
    d.connectToSocket(clopts['so-address'], clopts['port'])
    d.connectToDB(clopts['database'], clopts['db-user'], clopts['db-pass'], 
                  clopts['db-address'])

    while d.Running:
        d.insert(d.retrievePacket())


##A database daemon for transfering data.
#
# The daemon connects to a socket that supplies the data as packets which is
# then stored into a predetermined database.
#
class DBDaemon:

    ## Default Constructor.  Initializes instance variables to None
    def __init__(self):
             
    	##The socket reference 
    	self.Socket = None
    	##The database connection
    	self.Database = None
    	##Allows operations on database
    	self.Cursor = None
    	##Keeps the daemon running if true
    	self.Running = True
	
    def __del__(self):
    	self.disconnect()

    ##Makes a connection to a socket
    #
    # Establishes a connection to a socket, the serial forwarder, with the
    # forwarders IP and port numbers
    #
    # @param args A tuple containing the sockets IPAddress and port number
    #
    def connectToSocket(self, *args):
    
        if len(args) == 2:
        
            #Create and connect to the socket
            self.Socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            self.Socket.settimeout(5)
            self.Socket.connect(args)
            
            packet = self.Socket.recv(29)
            
            # Secret handshake and finding good packet
            if len(packet) < 29 and packet == 'U ':
                print self.Socket.send(packet)

                # Get a couple of packets to fix stuff, need to 
                # check for the whole header, though, not just the
                # first byte
                byte = self.Socket.recv(1)
                while ord(byte) != 0x1c:
                    byte = self.Socket.recv(1)
                    
                self.Socket.recv(28)

                # Make sure we are aligned on a packet
                packet = self.Socket.recv(29)
                if ord(packet[0]) != 0x1c:
                    print "Error aligning packets!"
                    sys.exit()
                print "Connected to Serial Forwarder"


            elif not(len(packet) == 29 and ord(packet[0]) == 0x1c):
                print "Handshake not recognized.  Do you really want to continue?"
        else:
            print "Argument is not a tuple with two elements!"
        
    ##Makes a connection to a database
    #
    # Establishes a connection to the database using the databases name, a username
    # and a key.
    #
    # @param db The database name 
    # @param userName The user name set up for accessing the database
    # @param passWord The password set up for accessing the database
    # @param hostName The address to the database
    #
    def connectToDB(self, db = 'drawbridge', userName = 'drbgdaemon', passWord = 'secret', hostName = 'localhost'):
        
        #Connect to database
        self.Database = psycopg2.connect(database = db, user = userName, password = passWord, host = hostName)
        self.Cursor = self.Database.cursor()
        
    ##Disconnects from a connected database and socket
    #
    # Checks first if there is a connection and if so disconnects from the database and the socket.
    #
    def disconnect(self):
        
        # close the connection to the database and socket
        if self.Cursor != None:
            self.Cursor.close()
        if self.Socket != None:
            self.Socket.close()
        if self.Database != None:
            self.Database.close()

    ##Shuts the daemon down.
    #
    # Calls the daemons destructor and then exits the system.
    # 
    def shutDown(self):
	
	del self
	sys.exit()
 
    ##Inserts a packet of mote data into a database
    #
    # Puts data into the database by making queries to the database itself.  To do this several other queries 
    # must be made in order to retrieve the proper table name to insert the data into.  
    # 
    # @param packet The packet of data to be inserted
    #
    def insert(self, packet):
    
        dictionary = DataParser.decodePacket(packet)
        sensorID_q = "SELECT sensor_id FROM mote_conf WHERE mote_id = (%s) AND port = (%s);"
        sensorName_q = "SELECT sensor_name FROM sensors WHERE id = (%s);"
        insert_q = "INSERT INTO %s (mote_id, time_st, reading) VALUES (%s, %s, %s)"

        #Go through ports and insert values for the ports into the proper tables
        for k in range (8):
            #Query the mote_conf table for the proper sensor id
            data = (dictionary['id'], k)
            self.Cursor.execute(sensorID_q, data)
            sensor_id = self.Cursor.fetchone()
                 
            #Query the sensors table for the proper sensor table name
            self.Cursor.execute(sensorName_q, sensor_id)
            sensor_table = self.Cursor.fetchone()
             
            #Insert our value into the correct sensor table
            insert_vals = (sensor_table[0], dictionary['id'], datetime.now(), dictionary[k])
            self.Cursor.execute(insert_q, insert_vals)
        
        self.Database.commit()
        print 'Inserted packet'
        
    ##Retrieves a packet from the socket
    #
    # Grabs a packet of data off of the connected socket and returns it to the 
    # caller.
    #
    # @return The retrieved packet of data
    #
    def retrievePacket(self):
        try:
        
            packet = self.Socket.recv(29)

	    #If something isn't correct with the packet attempt to align the packets
	    while len(packet) < 29 or ord(packet[0]) != 0x1c:
	        print "Bad packet.  Attempting to align"

		#Suck in bytes until 0x1c is found, the head of a packet
                byte = self.Socket.recv(1)
                while ord(byte) != 0x1c:
		    byte = self.Socket.recv(1)

		#Then get the remaining part of the packet to re-align
		self.Socket.recv(28)
		packet = self.Socket.recv(29)

	    
            return packet
            
        except socket.timeout:
            print "Error recieving packets from socket!!"
            self.shutDown()

## Print out a usage note
#
#  Print out a short synopsis on how to use the DBDaemon, what options it
#  accepts.
def usage():
    print "Usage: DBDaemon.py [OPTION]..."
    print "Start the DrawBridge database daemon."
    print "Written for NMSU Senior Lab"
    print "\tby Jeremiah Davis, Bretton Murphy, and David Salas"
    print "\tSpring 2011"
    print "Options will be read from a configuration files if available."
    print "-a,\t--address=URL\t\tThe URL of the serial forwarder."
    print "-c,\t--config-file=FILENAME\t\tAn alternate configuration file."
    print "-p,\t--port=PORT\t\tThe port number of the serial forwarder."
    print "-A,\t--db-address\t\tThe URL of the database to connect to."
    print "-d,\t--database\t\tThe database to connect to."
    print "-U\t--db-user\t\tThe database user to log in as."
    print "-P\t--db-pass\t\tThe password for the database."
    print "-s\t--db-size\t\tThe size of the database."
    sys.exit()
 
# Get the command line arguments and start running main
if __name__ == '__main__':
    main(sys.argv[1:])

# Otherwise, I'm probably running from emacs testing it
else:
    d = DBDaemon()
    d.connectToSocket('localhost', 9002)
    #d.retrievePacket()
    print 'Connected'
    d.connectToDB()
    print 'Connected to DB'
    while True:
        print 'going to retrieve'
        packet = d.retrievePacket()
        print packet
        print 'getting another one'
        packet = d.retrievePacket()
        packet = d.retrievePacket()
        print len(packet)
        print str(packet)
        print 'inserting now'
        d.insert(packet)
        
        
